/*
 * Peripheral Simulator (PSim)
 *
 * Copyright (c) 2008 Avadh Patel
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 * ChangeLog@: Changes made by Dharmadhikari,Sandeep Mohan and Avadh, Patel
 * Graduate Students @ State University of New York,Binghamton. Dept of Computer Science
 * Faculty Advisor : Prof Ghose, Kanad
 */

#include <stdio.h>

#include <config.h>
#include <cpu.h>
#include <cpu-defs.h>
#include <cpu-all.h>
#include <hw.h>
#include <pc.h>
#include <pci.h>

#include <psim.h>
#include <psim_lib.h>

#include "ram.h"

typedef uint32_t pci_addr_t;
#include <pci_host.h>

typedef PCIHostState X48State;

//PSim Devices of x48 (MCH and ICH)
static SimDevice* x48_sim_dev;
static SimDevice* x48MemCtrl_sim_dev;
static SimDevice* PPCIEX_sim_dev;
static SimDevice* SPCIEX_sim_dev;
static SimDevice* PCIEDMI_sim_dev;
static SimDevice* ICH_DIM_sim_dev;
static SimDevice* ICH_PCI_Bridge_sim_dev;

//Unique simulation device id
static int x48_uuid;

//PCIEDevice of x48 (MCH and ICH)
static PCIEDevice *x48MemCtrl_dev;
static PCIEDevice *ix48_dev;
static PCIEDevice *x48_dev;
static PCIEDevice *SPCIEX_dev;
static PCIEDevice *PPCIEX_dev;
static PCIBridge *ICH_PCI_BRIDGE_dev;
static PCIE_RCRB *PCIEDMI_dev;

#define PCIEXBAR_ADDR 0x67h
#define DMIBAR_ADDR 0x6fh

static void x48_addr_writel(void* opaque, uint32_t addr, uint32_t val)
{
    X48State *s = opaque;
    s->config_reg = val;
}

static uint32_t x48_addr_readl(void* opaque, uint32_t addr)
{
    X48State *s = opaque;
    return s->config_reg;
}



/* return the global irq number corresponding to a given device irq
   pin. We could also use the bus number to have a more precise
   mapping. */
static int pci_slot_get_pirq(PCIEDevice *pci_dev, int irq_num)
{
    int slot_addend;
    slot_addend = (pci_dev->devfn >> 3) - 1;
    return (irq_num + slot_addend) & 3;
}

static uint32_t isa_page_descs[384 / 4];
static uint32_t pcie_page_descs[384 / 4];
static uint8_t smm_enabled;
static int pci_irq_levels[4];

static void update_pam(PCIEDevice *d, uint32_t start, uint32_t end, int r)
{
    uint32_t addr;

    //    printf("ISA mapping %08x-0x%08x: %d\n", start, end, r);
    switch(r) {
    case 3:
        /* RAM */
        cpu_register_physical_memory(start, end - start,
                                     start);
        break;
    case 1:
        /* ROM (XXX: not quite correct) */
        cpu_register_physical_memory(start, end - start,
                                     start | IO_MEM_ROM);
        break;
    case 2:
    case 0:
        /* XXX: should distinguish read/write cases */
        for(addr = start; addr < end; addr += 4096) {
            cpu_register_physical_memory(addr, 4096,
                                         isa_page_descs[(addr - 0xa0000) >> 12]);
        }
        break;
    }
}

static void x48MemCtrl_update_memory_mappings(PCIEDevice *d)
{
    int i, r;
    uint32_t smram, addr;

    update_pam(d, 0xf0000, 0x100000, (d->config[0x90] >> 4) & 3);
    for(i = 0; i < 12; i++) {
        r = (d->config[(i >> 1) + 0x91] >> ((i & 1) * 4)) & 3;
        update_pam(d, 0xc0000 + 0x4000 * i, 0xc0000 + 0x4000 * (i + 1), r);
    }
    smram = d->config[0x9d];
    if ((smm_enabled && (smram & 0x08)) || (smram & 0x40)) {
        cpu_register_physical_memory(0xa0000, 0x20000, 0xa0000);
    } else {
        for(addr = 0xa0000; addr < 0xc0000; addr += 4096) {
            cpu_register_physical_memory(addr, 4096,
                                         pcie_page_descs[(addr - 0xa0000) >> 12]);
        }
    }
}

//void x48_set_smm(PCIEDevice *d, int val)
void ix48_set_smm(int val)
{
  PCIEDevice* d = ix48_dev;
    val = (val != 0);
    if (smm_enabled != val) {
        smm_enabled = val;
        ix48_update_memory_mappings(d);
    }
}


/* XXX: suppress when better memory API. We make the assumption that
   no device (in particular the VGA) changes the memory mappings in
   the 0xa0000-0x100000 range */

void x48_init_memory_mappings()
{
  //PCIEDevice* d = x48_dev;
  PCIEDevice* d = x48MemCtrl_dev;
    int i;
    for(i = 0; i < 96; i++) {
//        pcie_page_descs[i] = cpu_get_physical_page_desc(0xa0000 + i * 0x1000);
        pcie_page_descs[i] = sim_get_physical_page_desc(0xa0000 + i * 0x1000);
    }
}



static void PPCIEX_init_memory_mappings()
{
  //PCIEDevice* d = x48_dev;
  PCIEDevice* d = PPCIEX_dev;
    int i;
    for(i = 0; i < 96; i++) {
//        pcie_page_descs[i] = cpu_get_physical_page_desc(0xa0000 + i * 0x1000);
        pcie_page_descs[i] = sim_get_physical_page_desc(0xa0000 + i * 0x1000);
    }
}


static void SPCIEX_init_memory_mappings()
{
  //PCIEDevice* d = x48_dev;
 PCIEDevice* d = SPCIEX_dev;
    int i;
    for(i = 0; i < 96; i++) {
//        pcie_page_descs[i] = cpu_get_physical_page_desc(0xa0000 + i * 0x1000);
        pcie_page_descs[i] = sim_get_physical_page_desc(0xa0000 + i * 0x1000);
    }
}


static void x48_write_config(PCIEDevice *d,
                                uint32_t address, uint32_t val, int len)
{
    /* XXX: implement SMRAM.D_LOCK */
    pci_default_write_config(d, address, val, len);
    if ((address >= 0x59 && address <= 0x5f) || address == 0x72)
        ix48_update_memory_mappings(d);
}

static void PPCIEX_save(QEMUFile *f, void *opaque)
{
    PCIEDevice *d = (PCIEDevice)opaque;
    int i;

    pcie_device_save(d, f);
    qemu_put_8s(f, &smm_enabled);

    for (i=0; i<4; i++)
        qemu_put_be32(f, pci_irq_levels[i]);
}

static void PPCIEX_load(QEMUFile *f, void *opaque, int version_id)
{
    PCIEDevice *d = (PCIEDevice)opaque;
    int ret, i;

    if (version_id > 2)
        return -EINVAL;
    ret = pcie_device_load(d, f);
    if (ret < 0)
        return ret;
    x48MemCtrl_update_memory_mappings(d);
    qemu_get_8s(f, &smm_enabled);

    if (version_id >= 2)
        for (i=0; i<4; i++)
            pci_irq_levels[i] = qemu_get_be32(f);

    return 0;
}

static void x48MemCtrl_save(QEMUFile *f, void *opaque)
{
    PCIEDevice *d = (PCIEDevice)opaque;
    pcie_device_save(d, f);
}

static void x48MemCtrl_load(QEMUFile *f, void *opaque, int version_id)
{
    PCIEDevice *d = (PCIEDevice)opaque;
    int ret;

    if (version_id > 2)
        return -EINVAL;
    ret = pcie_device_load(d, f);
    if (ret < 0)
        return ret;

    return 0;
}

static void SPCIEX_save(QEMUFile *f, void *opaque)
{
    PCIEDevice *d = (PCIEDevice)opaque;
    pcie_device_save(d, f);
}

static void SPCIEX_load(QEMUFile *f, void *opaque, int version_id)
{
    PCIEDevice *d = (PCIEDevice)opaque;
    int ret;

    if (version_id > 2)
        return -EINVAL;
    ret = pcie_device_load(d, f);
    if (ret < 0)
        return ret;
    return 0;
}

static void ICH_PCI_BRIDGE_save(QEMUFile *f, void *opaque)
{
    PCIBridge *d = opaque;
    pci_device_save(d->dev, f);
}

static void ICH_PCI_BRIDGE_load(QEMUFile *f, void *opaque, int version_id)
{
    PCIBridge *d = opaque;
    int ret;

    if (version_id > 2)
        return -EINVAL;
    ret = pci_device_load(d->dev, f);
    if (ret < 0)
        return ret;
    return 0;
}

//int is_pcie_conf_access(uint32_t addr)
//{
//    uint64_t pci_exbar;
//    uint8_t pci_ex_base_addr;
//    pci_exbar = *(x48MemCtrl_dev->config[PCIEXBAR_ADDR]);
//    pci_ex_base_addr = (pci_exbar >> 28) & 0xffh; 
//    return -1;
//}


//PSim - callback function to forward message from bridge to device
void x48_to_dev(Message* msg)
{
  msg->from = x48_sim_dev;
  //regardless of type of message, find the device from address
  //and forward the message to it
  SimDevice* dev = sim_find_dev_with_addr(msg->addr,msg->size);
  dev->in(msg);
}

//PSim - callback function to forward msg to CPU
void x48_to_cpu(Message* msg)
{
//  assert(data!=NULL);
//  Message* msg = (Message*)data;
  msg->from = x48_sim_dev;//sim_get_dev_by_id(x48_uuid);
//  SimDevice* dev = sim_find_dev_with_addr(msg->addr);
  SimDevice* cpu_dev = sim_get_cpu();
  cpu_dev->in(msg);
}

/* PSim - callback function to send response to Cache */
void x48_to_cache(Message* msg)
{
  msg->from = x48_sim_dev;
  SimDevice* cache = sim_get_cache();
  cache->in(msg);
}

/* PSim - handle cache miss requests - simulate RAM delay */
//Currently this function is not implemented for x48
void sim_x48_cache_request(Message* msg)
{
//  enum mem_cmd req_cmd;
//  if(msg->type == READ_MSG)
//    req_cmd = Read;
//  else if(msg->type == WRITE_MSG)
//    req_cmd = Write;
//
//  uint32_t lat = mem_access_latency( req_cmd,
//      msg->addr, msg->size, sim_get_clock());
//
//  uint32_t lat = 5;
//  msg->from = x48_sim_dev;
//  SimDevice* cache = sim_get_cache();
//  assert(cache->in);
//  sim_insert_cb(cache->in, msg, lat);
  sim_insert_cb(&x48_to_cache, msg, lat);
}

/* PSim - read_config callback function of x48 */
void sim_x48_read(Message* msg)
{
    uint32_t addr = msg->addr;
    void* opaque = msg->opaque;
    int size = msg->size;
    uint32_t data; 
    if(addr == 0xcf8 && size == 4) {
      data = x48_addr_readl(opaque, addr);
      msg->data = data;
      sim_insert_cb(&x48_to_cpu, msg, 1);
      return ;
    }
    else if(addr >= 0xcfc && addr < 0xcfc+4)
    {
      switch(size) {
	case 1: data = pci_host_data_readb(opaque, addr);
		break;
	case 2: data = pci_host_data_readw(opaque, addr);
		break;
	case 4: data = pci_host_data_readl(opaque, addr);
		break;
	default: assert(0);
      }
      msg->data = data;
      sim_insert_cb(&x48_to_cpu, msg, 1);
      return ;
    }
    //Request is not for the bridge - forward to device
    sim_insert_cb(&x48_to_dev, msg, 1);
}

/* PSim - write_config callback function of x48 */
void sim_x48_write(Message* msg)
{
    uint32_t addr = msg->addr;
    uint32_t data = msg->data;
    void* opaque = msg->opaque;
    int size = msg->size;
    if(addr == 0xcf8 && size == 4) {
      x48_addr_writel(opaque, addr, data);
      sim_insert_cb(&x48_to_cpu, msg, 1);
      return;
    }
    else if(addr >= 0xcfc && addr < 0xcfc+4)
    {
      switch(size) {
	case 1: pci_host_data_writeb(opaque, addr, data);
		break;
	case 2: pci_host_data_writew(opaque, addr, data);
		break;
	case 4: pci_host_data_writel(opaque, addr, data);
		break;
	default: assert(0);
      }
      sim_insert_cb(&x48_to_cpu, msg, 1);
      return;
    }
    //The request is not for the bridge, so we need to forward it..
    //Set the no_return flag in simulator
    //Set a callback function to forward message to device
    sim_insert_cb(&x48_to_dev, msg, 1);
}

/* PSim message in callback */
void x48_in(Message* in_msg)
{
//  Message* in_msg = (Message*)data;
  //Check where is message from
  SimDevice* in_dev = (SimDevice*)in_msg->from;
  switch(in_dev->type)
  {
    case CPU:
      if(in_msg->type == READ_MSG)
	sim_x48_read(in_msg);
      else if(in_msg->type == WRITE_MSG)
	sim_x48_write(in_msg);
      break;
    case CACHE:
      sim_x48_cache_request(in_msg);
      break;
    case PCI_BUS:
    case PCI_IDE:
    case PCI_NETWORK_CARD:
    case PCI_DEVICE:
      //this message should be forwarded to CPU
      sim_insert_cb(sim_get_cpu()->in, in_msg, 1);
      break;
    default:
      assert(0);
  }
  return;
}

/* MMIO Callback Functions */

/* DMI Register Functions */
static void
int check_dmi_addr(target_phys_addr_t addr)
{
    uint32_t dmi_base = *(x48MemCtrl_dev->config[DMIBAR_ADDR]);
    if(addr > dmi_base && addr < (dmi_base + (2^20)))
        return 1;
    return 0;
}

static void
x48_dmi_mmio_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
{
    if(check_dmi_addr(addr) == 0)
        return;
    PCIE_RCRB *pcie_dev = opaque;
    uint32_t reg_addr = addr & 0xfffff;
    pcie_dev->config[reg_addr] = val;
}

static void
x48_dmi_mmio_writew(void *opaque, target_phys_addr_t addr, uint32_t val)
{
    x48_dmi_mmio_writel(opaque, addr, val);
}

static void
x48_dmi_mmio_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
{
    x48_dmi_mmio_writel(opaque, addr, val);
}

static uint32_t
x48_dmi_mmio_readl(void *opaque, target_phys_addr_t addr, uint32_t val)
{
    if(check_dmi_addr(addr) == 0)
        return 0;
    PCIE_RCRB *pcie_dev = opaque;
    uint32_t reg_addr = addr & 0xfffff;
    return pcie_dev->config[reg_addr];
}

static uint32_t
x48_dmi_mmio_readw(void *opaque, target_phys_addr_t addr, uint32_t val)
{
    return x48_dmi_mmio_readl(opaque, addr, val);
}

static uint32_t
x48_dmi_mmio_readb(void *opaque, target_phys_addr_t addr, uint32_t val)
{
    return x48_dmi_mmio_readl(opaque, addr, val);
}

static CPUWriteMemoryFunc *x48_dmi_mmio_write[] = {
    x48_dmi_mmio_writeb, x48_dmi_mmio_writew, x48_dmi_mmio_writel
};

static CPUReadMemoryFunc *x48_dmi_mmio_read[] = {
    x48_dmi_mmio_readb, x48_dmi_mmio_readw, x48_dmi_mmio_readl
};

/* PCIE Config space MMIO Functions */
static void
int check_config_addr(target_phys_addr_t addr)
{
    uint32_t config_base = *(x48MemCtrl_dev->config[PCIEXBAR_ADDR]);
    if(addr > config_base && addr < (config_base + 256*(2^20)))
        return 1;
    return 0;
}

static void
x48_config_mmio_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
{
    if(check_config_addr(addr) == 0)
        return;
    PCIEDevice *pcie_dev = opaque;
    pcie_data_write(pcie_dev->pci_dev->bus, addr, val, 32);
}

static void
x48_config_mmio_writew(void *opaque, target_phys_addr_t addr, uint32_t val)
{
    if(check_config_addr(addr) == 0)
        return;
    PCIEDevice *pcie_dev = opaque;
    pcie_data_write(pcie_dev->pci_dev->bus, addr, val, 16);
}

static void
x48_config_mmio_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
{
    if(check_config_addr(addr) == 0)
        return;
    PCIEDevice *pcie_dev = opaque;
    pcie_data_write(pcie_dev->pci_dev->bus, addr, val, 8);
}

static uint32_t
x48_config_mmio_readl(void *opaque, target_phys_addr_t addr, uint32_t val)
{
    if(check_config_addr(addr) == 0)
        return 0;
    PCIEDevice *pcie_dev = opaque;
    return pcie_data_read(pcie_dev->pci_dev->bus, addr, val, 32);
}

static uint32_t
x48_config_mmio_readw(void *opaque, target_phys_addr_t addr, uint32_t val)
{
    if(check_config_addr(addr) == 0)
        return 0;
    PCIEDevice *pcie_dev = opaque;
    return pcie_data_read(pcie_dev->pci_dev->bus, addr, val, 16);
}

static uint32_t
x48_config_mmio_readb(void *opaque, target_phys_addr_t addr, uint32_t val)
{
    if(check_config_addr(addr) == 0)
        return 0;
    PCIEDevice *pcie_dev = opaque;
    return pcie_data_read(pcie_dev->pci_dev->bus, addr, val, 8);
}

static CPUWriteMemoryFunc *x48_config_mmio_write[] = {
    x48_config_mmio_writeb, x48_config_mmio_writew, x48_config_mmio_writel
};

static CPUReadMemoryFunc *x48_config_mmio_read[] = {
    x48_config_mmio_readb, x48_config_mmio_readw, x48_config_mmio_readl
};
/* End MMIO Callback Functions */

/* x48 Init function */
//This function will initalize all the sub devices of the x48 chipset
//And it will also initialize a ISA bridge PIIX-3
void x48_init()
{
    PCIBus *b;
    x48State *s;

    s = qemu_mallocz(sizeof(x48State));//SMD: Make this structure
    qemu_irq* pic = sim_get_pic();//pcie as a bus register: but register as pcie

    b = pci_register_bus(x48_set_irq, pci_slot_get_pirq, pic, 0, 4);//bus 0
    s->bus = b;

    register_ioport_write(0xcf8, 4, 4, x48_addr_writel, s);//sizeof byte
    register_ioport_read(0xcf8, 4, 4, x48_addr_readl, s);//sizeof byte

    register_ioport_write(0xcfc, 4, 1, pci_host_data_writeb, s);
    register_ioport_write(0xcfc, 4, 2, pci_host_data_writew, s);
    register_ioport_write(0xcfc, 4, 4, pci_host_data_writel, s);
    register_ioport_read(0xcfc, 4, 1, pci_host_data_readb, s);
    register_ioport_read(0xcfc, 4, 2, pci_host_data_readw, s);
    register_ioport_read(0xcfc, 4, 4, pci_host_data_readl, s);

    sim_register_init_mem_map_cb(x48_init_memory_mappings);//used for memory map device: when os boots this function is called
    sim_register_smm_cb(ix48_set_smm);

    //Register all the devices of x48 chipset
    x48MemCtrl_sim_dev = sim_register_device(PCI_DEVICE, "x48_Mem_Controller", x48_sim_dev->dl_handler, 0);
    PPCIEX_sim_dev = sim_register_device(PCI_DEVICE, "x48_PPCI_bridge", x48_sim_dev->dl_handler, 0);
    SPCIEX_sim_dev = sim_register_device(PCI_DEVICE, "x48_SPCI_bridge", x48_sim_dev->dl_handler, 0);
    PCIEDMI_sim_dev = sim_register_device(PCI_DEVICE, "x48_PCI_DMI", x48_sim_dev->dl_handler, 0);
    ICH_DIM_sim_dev = sim_register_device(PCI_DEVICE, "x48_ICH_DMI", x48_sim_dev->dl_handler, 0);
    ICH_PCI_Bridge_sim_dev = sim_register_device(PCI_DEVICE, "x48_ICH_PCI_Bridge", x48_sim_dev->dl_handler, 0);

    //Device 0
    //SMD:DRAM Controller Interface device 
    //Bus 0, Device 0
    PCIEDevice *d;//x48MemCtrl
	d = pcie_register_device(b, "x48MemCtrl", sizeof(PCIEDevice), 0, NULL, NULL);
    sim_register_qemu_device(x48MemCtrl_sim_dev, d);

    d->config[0x00] = 0x86; // vendor_id
    d->config[0x01] = 0x80;
    d->config[0x02] = 0xe0; // device_id
    d->config[0x03] = 0x29;
    d->config[0x08] = 0x01; // revision
    d->config[0x0a] = 0x00; // class_sub 
    d->config[0x0b] = 0x06; // class_base
    d->config[0x0e] = 0x00; // header_type
    d->config[0x9d] = 0x02; /* SMRAM */
    register_savevm("x48MemCtrl", 0, 2, x48MemCtrl_save, x48MemCtrl_load, d);//SMD: pause and check vm
    x48MemCtrl_dev = d;
    sim_reigster_qemu_device(x48MemCtrl_sim_dev, x48MemCtrl_dev);
    x48MemCtrl_reset(d);

    //Register MMIO config spance and MMIO DMI config space
    int config_mmio_index = cpu_register_io_memory(0, x48_config_mmio_read,
            x48_config_mmio_write, d);
    uint32_t config_base_addr = d->config[PCIEXBAR_ADDR];
    cpu_register_physical_memory(config_base_addr, 256*(2^20), config_mmio_index);

    //Device 1
    //Primary Host-PCI Express Bridge
    //Bus 0, Device 1
    PCIEDevice *e;//PPCIX-> Primary PCI Express
    e = pcie_register_device(b, "PPCIEX", sizeof(PCIEDevice), 1,
                            NULL, x48_write_config);
    e->config[0x00] = 0x86; // vendor_id
    e->config[0x01] = 0x80;
    e->config[0x02] = 0xe1; // device_id
    e->config[0x03] = 0x29;
    e->config[0x06] = 0x10;
    e->config[0x08] = 0x01; // revision
    e->config[0x0a] = 0x04; // class_sub 
    e->config[0x0b] = 0x06; // class_base
    e->config[0x0e] = 0x01; // header_type
    e->config[0x1c] = 0xf0; 
    
    register_savevm("PPCIEX", 0, 2, PPCIEX_save, PPCIEX_load, e);
    PPCIEX_dev = e;
    sim_register_qemu_device(PPCIEX_sim_dev, PPCIEX_dev);
    sim_register_addr(PPCIEX_sim_dev, 0xcf8, 4, 4);
    sim_register_addr(PPCIEX_sim_dev, 0xcfc, 4, 1);
    sim_register_addr(PPCIEX_sim_dev, 0xcfc, 4, 2);
    sim_register_addr(PPCIEX_sim_dev, 0xcfc, 4, 4);
    sim_set_pci_bridge_dev(PPCIEX_dev);
    
    PPCIEX_reset(e);

    //Device 2
    //SMD: Secondary Host PCI Express Bridge
    //Bus 0 Device 6
    PCIEDevice *f;//SPCIX-> Secondary PCI Express
    f = pcie_register_device(b, "SPCIEX", sizeof(PCIEDevice), 6, NULL, NULL);

    f->config[0x00] = 0x86; // vendor_id
    f->config[0x01] = 0x80;
    f->config[0x02] = 0xe9; // device_id
    f->config[0x03] = 0x29;
    f->config[0x06] = 0x10;
    f->config[0x08] = 0x01; // revision
    f->config[0x0a] = 0x04; // class_sub 
    f->config[0x0b] = 0x06; // class_base
    f->config[0x0e] = 0x01; // header_type
    f->config[0x1c] = 0xf0; 

    register_savevm("SPCIEX", 0, 2, SPCIEX_save, SPCIEX_load, f);//SMD: pause and check vm
    SPCIEX_dev = f;
    sim_register_qemu_device(SPCIEX_sim_dev, SPCIEX_dev);
    SPCIEX_reset(f);

	
    //Device 3
    //DMI-PCI
    PCIEDMI_dev = qemu_mallocz(sizeof(PCIE_RCRB));
    DMIDevice_reset(PCIEDMI_dev);
//    sim_register_qemu_device(PCIEDMI_dev, PCIEDMI_sim_dev);
//    register_savevm("PCIEDMI", 0, 2, PCIEDMI_save, PCIEDMI_load, PCIEDMI_dev);

    int dmi_mmio_index = cpu_register_io_memory(0, x48_dmi_mmio_read, 
            x48_dmi_mmio_write, PCIEDMI_dev);
    uint32_t dmi_base_addr = d->config[DMIBAR_ADDR];
    cpu_register_physical_memory(dmi_base_addr, 2^20, dmi_mmio_index);


    //ICH PCI Bridge Device
    //All standard PCI devices will connect through this PCI bridge
    ICH_PCI_BRIDGE_dev = (PCIBridge*)pci_register_device(b, "ICHPCIBRIDGE",
            sizeof(PCIBridge), 30, NULL, pci_bridge_write_config);
    ICH_PCIPCIBrdg_reset(ICH_PCI_BRIDGE_dev);
    sim_register_qemu_device(ICH_PCI_Bridge_sim_dev, ICH_PCI_BRIDGE_dev);
    register_savevm("ICHPCIBRG", 0, 2, ICH_PCI_BRIDGE_save, ICH_PCI_BRIDGE_load, ICH_PCI_BRIDGE_dev);

    ICH_PCI_BRIDGE_dev->bus = pci_register_secondary_bus(
            &ICH_PCI_BRIDGE_dev->dev, pci_slot_get_pirq);
    sim_set_pci_bus(ICH_PCI_BRIDGE_dev->bus);

    //initialize the ISA bridge..
    piix3_init();    

    return;
}
/* End x48_init() */

/* ISA Bridge PIIX3 Functions */
static void piix_save(QEMUFile* f, void *opaque)
{
    PCIDevice *d = opaque;
    pci_device_save(d, f);
}

static int piix_load(QEMUFile* f, void *opaque, int version_id)
{
    PCIDevice *d = opaque;
    if (version_id != 2)
        return -EINVAL;
    return pci_device_load(d, f);
}

static void piix3_reset(PCIDevice *d)
{
    uint8_t *pci_conf = d->config;

    pci_conf[0x04] = 0x07; // master, memory and I/O
    pci_conf[0x05] = 0x00;
    pci_conf[0x06] = 0x00;
    pci_conf[0x07] = 0x02; // PCI_status_devsel_medium
    pci_conf[0x4c] = 0x4d;
    pci_conf[0x4e] = 0x03;
    pci_conf[0x4f] = 0x00;
    pci_conf[0x60] = 0x80;
    pci_conf[0x69] = 0x02;
    pci_conf[0x70] = 0x80;
    pci_conf[0x76] = 0x0c;
    pci_conf[0x77] = 0x0c;
    pci_conf[0x78] = 0x02;
    pci_conf[0x79] = 0x00;
    pci_conf[0x80] = 0x00;
    pci_conf[0x82] = 0x00;
    pci_conf[0xa0] = 0x08;
    pci_conf[0xa2] = 0x00;
    pci_conf[0xa3] = 0x00;
    pci_conf[0xa4] = 0x00;
    pci_conf[0xa5] = 0x00;
    pci_conf[0xa6] = 0x00;
    pci_conf[0xa7] = 0x00;
    pci_conf[0xa8] = 0x0f;
    pci_conf[0xaa] = 0x00;
    pci_conf[0xab] = 0x00;
    pci_conf[0xac] = 0x00;
    pci_conf[0xae] = 0x00;
}

//int piix3_init(PCIBus *bus, int devfn)
void piix3_init()
{
    PCIDevice *d;
    uint8_t *pci_conf;

    PCIBus* bus = sim_get_pci_bus();
    int devfn = -1;

    d = pci_register_device(bus, "PIIX3", sizeof(PCIDevice),
                                    devfn, NULL, NULL);
    register_savevm("PIIX3", 0, 2, piix_save, piix_load, d);

    piix3_dev = d;
    pci_conf = d->config;

    pci_conf[0x00] = 0x86; // Intel
    pci_conf[0x01] = 0x80;
    pci_conf[0x02] = 0x00; // 82371SB PIIX3 PCI-to-ISA bridge (Step A1)
    pci_conf[0x03] = 0x70;
    pci_conf[0x0a] = 0x01; // class_sub = PCI_ISA
    pci_conf[0x0b] = 0x06; // class_base = PCI_bridge
    pci_conf[0x0e] = 0x80; // header_type = PCI_multifunction, generic

    piix3_reset(d);

    sim_set_isa_bridge_dev(d);

    return;
}
/* End ISA Functions */

/* PSim's load function */
void device_load(void* _dl_handler)
{
    //Here we will register only the x48_sim_dev and in init we will register
    //other sub devices
    x48_sim_dev = sim_register_device(PCI_BRIDGE, "x48", _dl_handler, 0);
    sim_register_init_cb(x48_sim_dev, &x48_init);
    sim_register_in_cb(x48_sim_dev, &x48_in);

}



/* just used for simpler irq handling. */
#define PCI_IRQ_WORDS   ((PCI_DEVICES_MAX + 31) / 32)

static void x48_set_irq(qemu_irq *pic, int irq_num, int level)
{
    int i, pic_irq, pic_level;
	//SMD:Do this for the primary PCI express bridge
    e->config[0x3d] = 0x01;   // enable bit
    pci_irq_levels[irq_num] = level;

    /* XXX: optimize */
    pic_irq = e->config[0x3d];
    if (pic_irq < 16) {
        /* The pic level is the logical OR of all the PCI irqs mapped
           to it */
        pic_level = 0;
        i=0;//for (i = 0; i < 4; i++) {
            if (pic_irq == e->config[0x3d])
                pic_level |= pci_irq_levels[i];
        }
        qemu_set_irq(pic[pic_irq], pic_level);
    }
}


/* Devices Configuration Space Rest Functions */
static void x48MemCtrl_reset(PCIEDevice *d)
{
    uint8_t *pci_conf = d->config;

    pci_conf[0x04] = 0x06; // master, memory and I/O
    pci_conf[0x05] = 0x00;
    pci_conf[0x06] = 0x90;
    pci_conf[0x07] = 0x00; // PCI_status_devsel_medium
    
    pci_conf[0x0b] = 0x06;
    pci_conf[0x34] = 0xe0;
    pci_conf[0x54] = 0xdb;
    pci_conf[0x55] = 0x23;

    pci_conf[0x63] = 0xe0;
    pci_conf[0x69] = 0x00;

    pci_conf[0xe0] = 0x09;
    pci_conf[0xe2] = 0x0c;
    pci_conf[0xe3] = 0x01;
    pci_conf[0xe5] = 0x40;
    pci_conf[0xe6] = 0x06;
    pci_conf[0xe7] = 0x81;
    pci_conf[0xe8] = 0x01;
}





static void PPCIEX_reset(PCIEDevice *d)
{
    uint8_t *pci_conf = d->config;

    pci_conf[0x04] = 0x00; // master, memory and I/O
    pci_conf[0x05] = 0x00;
    pci_conf[0x06] = 0x10;
    pci_conf[0x08] = 0x01;
    pci_conf[0x0a] = 0x04;    
    pci_conf[0x0b] = 0x06;

    pci_conf[0x0e] = 0x01;
    pci_conf[0x1c] = 0xf0;

    pci_conf[0x20] = 0x0f;
    pci_conf[0x21] = 0xff;

    pci_conf[0x24] = 0xf1;
    pci_conf[0x25] = 0xff;

    pci_conf[0x26] = 0x01;


    pci_conf[0x34] = 0x88;

    pci_conf[0x3d] = 0x01;


    pci_conf[0x80] = 0x01;
    pci_conf[0x81] = 0x90;
    pci_conf[0x82] = 0x03;
    pci_conf[0x83] = 0xc8;

    pci_conf[0x84] = 0x08;

    pci_conf[0x88] = 0x0d;
    pci_conf[0x89] = 0x80;

    pci_conf[0x8c] = 0x86;
    pci_conf[0x8d] = 0x80;

    pci_conf[0x90] = 0x05;
    pci_conf[0x91] = 0xa0;

    pci_conf[0xa0] = 0x10;

    pci_conf[0xa2] = 0x42;
    pci_conf[0xa3] = 0x01;

    pci_conf[0xa5] = 0x80;


    pci_conf[0xac] = 0x02;
    pci_conf[0xad] = 0x4d;
    pci_conf[0xae] = 0x21;
    pci_conf[0xaf] = 0x20;

    pci_conf[0xb3] = 0x10;

    pci_conf[0xb6] = 0x04;

    pci_conf[0x100] = 0x02;
    pci_conf[0x102] = 0x01;
    pci_conf[0x103] = 0x14;

    pci_conf[0x110] = 0x01;

    pci_conf[0x114] = 0xff;
    pci_conf[0x117] = 0x80;

    pci_conf[0x11a] = 0x02;

    pci_conf[0x140] = 0x05;
    pci_conf[0x142] = 0x01;

    pci_conf[0x145] = 0x01;
    pci_conf[0x147] = 0x02;


    pci_conf[0x218] = 0xff;
    pci_conf[0x219] = 0x0f;

}


static void SPCIEX_reset(PCIEDevice *d)
{
    uint8_t *pci_conf = d->config;

    pci_conf[0x04] = 0x00; // master, memory and I/O
    pci_conf[0x05] = 0x00;
    pci_conf[0x06] = 0x10;
    pci_conf[0x08] = 0x01;


    pci_conf[0x0a] = 0x04;    
    pci_conf[0x0b] = 0x06;

    pci_conf[0x0e] = 0x01;

    pci_conf[0x1c] = 0xf0;

    pci_conf[0x20] = 0x0f;
    pci_conf[0x21] = 0xff;

    pci_conf[0x24] = 0xf1;
    pci_conf[0x25] = 0xff;

    pci_conf[0x26] = 0x01;


    pci_conf[0x34] = 0x88;

    pci_conf[0x3d] = 0x01;


    pci_conf[0x80] = 0x01;
    pci_conf[0x81] = 0x90;
    pci_conf[0x82] = 0x03;
    pci_conf[0x83] = 0xc8;

    pci_conf[0x84] = 0x08;

    pci_conf[0x88] = 0x0d;
    pci_conf[0x89] = 0x80;

    pci_conf[0x8c] = 0x86;
    pci_conf[0x8d] = 0x80;

    pci_conf[0x90] = 0x05;
    pci_conf[0x91] = 0xa0;

    pci_conf[0xa0] = 0x10;

    pci_conf[0xa2] = 0x42;
    pci_conf[0xa3] = 0x01;

    pci_conf[0xa5] = 0x80;

    pci_conf[0xac] = 0x02;
    pci_conf[0xad] = 0x4d;
    pci_conf[0xae] = 0x21;
    pci_conf[0xaf] = 0x03;


    pci_conf[0xb3] = 0x10;


    pci_conf[0xb6] = 0x04;

    pci_conf[0x100] = 0x02;
    pci_conf[0x102] = 0x01;
    pci_conf[0x103] = 0x14;

    pci_conf[0x114] = 0xff;
    pci_conf[0x117] = 0x80;
    pci_conf[0x11a] = 0x02;


    pci_conf[0x140] = 0x05;
    pci_conf[0x142] = 0x01;

    pci_conf[0x145] = 0x01;
    pci_conf[0x147] = 0x03;

}


static void DMIDevice_reset(PCIE_RCRB *d)
{
    uint8_t *pci_conf = d->config;

    pci_conf[0x00] = 0x86;
    pci_conf[0x01] = 0x80;
    pci_conf[0x02] = 0x00;//SMD:This value from
    pci_conf[0x03] = 0x00;//Intel ich datasheet

    pci_conf[0x04] = 0x01;//VC compatibility register 4-7h

    pci_conf[0x10] = 0x01;

    pci_conf[0x14] = 0xff;

    pci_conf[0x17] = 0x80;

    pci_conf[0x1a] = 0x02;

    pci_conf[0x1c] = 0x01;
    pci_conf[0x1d] = 0x80;

    pci_conf[0x23] = 0x01;

    pci_conf[0x26] = 0x02;

    pci_conf[0x84] = 0x41;
    pci_conf[0x85] = 0x2c;
    pci_conf[0x86] = 0x01;

    pci_conf[0x8a] = 0x01;
}

static void ICH_DMI_reset(PCIDevice *d)
{
    uint8_t *pci_conf = d->config;

    pci_conf[0x00] = 0x02;
    pci_conf[0x01] = 0x00;
    pci_conf[0x02] = 0x01;
    pci_conf[0x03] = 0x04;

    
    pci_conf[0x0c] = 0x00;
    pci_conf[0x0d] = 0x00;

    pci_conf[0x10] = 0x01;

    pci_conf[0x14] = 0xff;
    pci_conf[0x17] = 0x80;

    pci_conf[0x1a] = 0x02;


    pci_conf[0x1c] = 0x01;
    pci_conf[0x1d] = 0x80;

    pci_conf[0x23] = 0x01;

    pci_conf[0x26] = 0x02;

    pci_conf[0x84] = 0x41;
    pci_conf[0x85] = 0x2c;
    pci_conf[0x86] = 0x01;


    pci_conf[0x8a] = 0x01;

}


static void ICH_PCIPCIBrdg_reset(PCIBridge *d)
{
    uint8_t *pci_conf = d->dev.config;

    pci_conf[0x00] = 0x86;
    pci_conf[0x01] = 0x80;
    pci_conf[0x02] = 0x00;//SMD:This value from
    pci_conf[0x03] = 0x00;//Intel ich datasheet

    
    pci_conf[0x06] = 0x10;

    pci_conf[0x08] = 0x01;

    pci_conf[0x09] = 0x01;
    pci_conf[0x0a] = 0x04;
    pci_conf[0x0b] = 0x06;


    pci_conf[0x0e] = 0x01;


    pci_conf[0x1c] = 0x01;
    pci_conf[0x1d] = 0x80;

    pci_conf[0x1e] = 0x80;
    pci_conf[0x1f] = 0x02;

    pci_conf[0x24] = 0x01;
    pci_conf[0x26] = 0x01;


    pci_conf[0x34] = 0x50;


    pci_conf[0x4d] = 0x12;
    pci_conf[0x50] = 0x0d;

}
/* End Configuartion Space Reset Functions */
